import {
  Component,
  ContentChild,
  Input,
  forwardRef,
  ViewChild,
  HostBinding,
  OnInit,
  NgZone,
  ChangeDetectorRef,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ConnectedOverlayPositionChange } from '@angular/cdk/overlay';

import {
  OverlayEntry,
  Placement,
  composePositionSet,
  POSITION_MAP,
  DEFAULT_PICKER_POSITIONS,
  HorizontalShowHideAnimation,
  VerticalShowHideAnimation,
} from '../shared/shared';
import { TimepickerInnerComponent } from '../timepicker/timepicker.module';
import { CalendarDay, CalendarMonth } from '../calendar-lite/calendar-lite.module';
import { DatepickerLiteContentDirective } from './datepicker-lite-content.directive';

@Component({
  selector: 'fui-datepicker-lite',
  templateUrl: './datepicker-lite.component.html',
  animations: [
    HorizontalShowHideAnimation,
    VerticalShowHideAnimation,
  ],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => DatepickerLiteComponent),
      multi: true,
    },
  ],
})
export class DatepickerLiteComponent extends OverlayEntry implements OnInit, ControlValueAccessor {
  @HostBinding('class.fui-datepicker-lite') hostClass = true;

  @ViewChild('trigger') trigger;

  @ViewChild(TimepickerInnerComponent) timepicker: TimepickerInnerComponent;

  /** Custom content given by <ng-template fui-datepicker-lite-content>. */
  @ContentChild(DatepickerLiteContentDirective) contentTemplate: DatepickerLiteContentDirective;

  /** Show time picker or not. */
  @Input() showTime = false;

  /** Date time format. */
  @Input() format = 'yyyy-MM-dd HH:mm';

  /** Required field in form. */
  @Input() required = false;

  /** Placeholder in input. */
  @Input() placeholder: string;

  /** Datepicker popup placement. */
  @Input() placement: Placement = 'bottomLeft';

  /** Define which date is disabled. */
  @Input() disabledDate: (date: Date) => boolean;

  mode: 'decade' | 'year' | 'month' | 'day' | 'time' = 'day';
  today = new Date();
  selectedMonth = this.value.getMonth();
  selectedYear = this.value.getFullYear();
  selectedDate = this.value.getDate();
  showMonth = (new Date()).getMonth();
  showYear = (new Date()).getFullYear();
  startDecade = Math.floor(this.showYear / 10) * 10;
  yearPanel: Array<Array<string>> = [];
  // ngModel Access
  onChange: any = Function.prototype;
  onTouched: any = Function.prototype;
  classMap = {};

  currentDate = new Date();
  valueSource: Date = null;
  set value(value: Date) {
    if (value && typeof value.getMonth !== 'function') {
      value = new Date(value);
    }
    this.updateValue(value);
  }
  get value(): Date {
    if (!this.currentDate) {
      this.currentDate = new Date();
    }
    return this.valueSource || this.currentDate;
  }

  get isHorizontal(): boolean {
    const directionFirstWord = this.direction[0];
    return directionFirstWord === 'l' || directionFirstWord === 'r';
  }

  constructor(
    private cdr: ChangeDetectorRef,
  ) {
    super();
  }

  ngOnInit() {
    this.generateYearPanel();
    this.positions = composePositionSet(this.placement);
    this.setPosition(this.placement);

    this.visible.subscribe((visible: boolean) => {
      if (visible) {
        this.mode = 'day';
      }
    });
  }

  clearCalendar($event) {
    $event.stopPropagation();
    this.value = null;
    this.onChange(null);
  }

  changeTime($event) {
    this.valueSource = $event;
    this.onChange(this.value);
  }

  preYear() {
    this.showYear = this.showYear - 1;
  }

  nextYear() {
    this.showYear = this.showYear + 1;
  }

  preMonth() {
    if (this.showMonth - 1 < 0) {
      this.showMonth = 11;
      this.preYear();
    } else {
      this.showMonth = this.showMonth - 1;
    }
  }

  nextMonth() {
    if (this.showMonth + 1 > 11) {
      this.showMonth = 0;
      this.nextYear();
    } else {
      this.showMonth = this.showMonth + 1;
    }
  }

  setShowYear(year, $event) {
    $event.stopPropagation();
    this.showYear = year;
    this.mode = 'year';
  }

  preDecade() {
    this.startDecade = this.startDecade - 10;
  }

  nextDecade() {
    this.startDecade = this.startDecade + 10;
  }

  changeToToday() {
    this.currentDate = new Date();
    this.value = this.currentDate;
    this.onChange(this.valueSource);
    this.hide();
  }

  clickDay(day: CalendarDay) {
    if (!this.showTime) {
      this.hide();
      this.value = day.date;
      this.onChange(this.valueSource);
    } else {
      const currentValue = new Date(this.value);
      currentValue.setFullYear(day.date.getFullYear());
      currentValue.setMonth(day.date.getMonth());
      currentValue.setDate(day.date.getDate());
      this.value = currentValue;
      this.onChange(this.value);
    }
  }

  clickMonth(month: CalendarMonth) {
    const currentValue = new Date(this.value);
    currentValue.setFullYear(this.showYear);
    currentValue.setMonth(month.index);
    this.value = currentValue;
    this.onChange(this.valueSource);
    this.showMonth = month.index;
    this.mode = 'year';
  }

  changeMonthView() {
    this.mode = 'month';
  }

  changeDecadeView($event) {
    $event.stopPropagation();
    this.mode = 'decade';
  }

  changeTimeView($event) {
    $event.stopPropagation();
    this.mode = 'time';
    setTimeout(() => {
      this.timepicker.initPosition();
    });
  }

  changeYearView($event) {
    $event.stopPropagation();
    this.mode = 'year';
  }

  generateYearPanel() {
    let _t = [];
    for (let i = 0; i < 10; i++) {
      if (i === 1 || i === 4 || i === 7 || i === 9) {
        _t.push(i);
        this.yearPanel.push(_t);
        _t = [];
      } else {
        _t.push(i);
      }
    }
    this.yearPanel[ 0 ].unshift('start');
    this.yearPanel[ 3 ].push('end');
  }

  setPosition(placement: Placement) {
    this.direction = this.toDirection(placement);
    this.classMap = {
      [`fui-datepicker-lite-${this.toShortDirection(this.direction)}`]: true,
    };
    this.cdr.detectChanges();
  }

  toShortDirection(direction: string) {
    const dirSnakes = this.toSnake(direction);
    return dirSnakes.split('-')[0];
  }

  writeValue(value: any): void {
    if (value && typeof value.getMonth !== 'function') {
      value = new Date(value);
    }
    this.updateValue(value);
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  private updateValue(value: any) {
    if (this.valueSource === value) {
      return;
    }
    this.valueSource = value;
    this.selectedMonth = this.value.getMonth();
    this.selectedYear = this.value.getFullYear();
    this.selectedDate = this.value.getDate();
    this.showYear = this.value.getFullYear();
    this.showMonth = this.value.getMonth();
    this.startDecade = Math.floor(this.showYear / 10) * 10;
  }

}
